import cv2
import mediapipe as mp

mpDraw = mp.solutions.drawing_utils
import math

tip_ids = [4, 8, 12, 16, 20]


## 导入模型
def load_model():
    global mp_hands, hands
    mp_hands = mp.solutions.hands
    hands = mp_hands.Hands(static_image_mode=False,  # 是静态图片还是连续视频帧
                           max_num_hands=1,  # 最多检测几只手
                           min_detection_confidence=0.7,  # 置信度阈值
                           min_tracking_confidence=0.5)  # 追踪阈值


def infer_image(img):
    # BGR转RGB
    img_RGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    h, w, _ = img_RGB.shape
    count = 0  # 统计手指的伸出个数
    # 将RGB图像输入模型，获取预测结果
    results = hands.process(img_RGB)
    if results.multi_hand_landmarks:
        for handlms in results.multi_hand_landmarks:
            finger = []
            finger_point = []

            # 这里我将每一根手指的四个坐标整合到一个列表中，大家可以打印finger，进行详细的查看
            for id, lm in enumerate(handlms.landmark):
                x, y = int(lm.x * w), int(lm.y * h)
                if id == 0:
                    pass
                elif id % 4 == 0:
                    finger_point.append([x, y])
                    finger.append(finger_point)
                    finger_point = []
                else:
                    finger_point.append([x, y])

                # 遍历每一根手指列表，计算其构成的三角形的三边长，这里使用2，6，10，14，18所对应的角进行判断
            for id, point in enumerate(finger):
                a = math.hypot((point[0][0] - point[1][0]), (point[0][1] - point[1][1]))
                b = math.hypot((point[1][0] - point[2][0]), (point[1][1] - point[2][1]))
                c = math.hypot((point[0][0] - point[2][0]), (point[0][1] - point[2][1]))

                # 在计算value值的时候，除数可能为零，以及当三点在一点直线上，都会抛出异常，所以要捕获异常
                try:
                    value = (a ** 2 + b ** 2 - c ** 2) / (2 * a * b)
                    angle = math.acos(value) * 57
                except ValueError:
                    angle = 180
                except ZeroDivisionError:
                    angle = 0

                # 当角度大于155的时候记为手指伸出
                if angle >= 155:
                    count += 1
                else:
                    pass

            # 在手部绘制关键点位置
            mpDraw.draw_landmarks(img, handlms, mp_hands.HAND_CONNECTIONS)
        # 将手指检测的结果显示在图像上
        cv2.putText(img, str(count), (int((1 / 9) * w), int((1 / 9) * h)), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 0, 0), 1)
    if not results.multi_hand_landmarks:
        count = -1

    return [img, count]


if __name__ == "__main__":
    load_model()
    cap = cv2.VideoCapture(0)

    # 打开cap
    cap.open(0)

    # 无限循环，直到break被触发
    while cap.isOpened():
        # 获取画面
        success, frame = cap.read()
        if not success:
            break
        frame = cv2.flip(frame, 1)
        ## !!!处理帧函数
        frame, c = infer_image(frame)

        # 展示处理后的三通道图像
        cv2.imshow('my_window', frame)

        if cv2.waitKey(1) in [ord('q'), 27]:  # 按键盘上的q或esc退出（在英文输入法下）
            break

    # 关闭摄像头
    cap.release()

    # 关闭图像窗口
    cv2.destroyAllWindows()

#fuck you
